package com.better.common.utils.redis;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.better.common.utils.json.JsonMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.connection.SortParameters;
import org.springframework.data.redis.core.*;
import org.springframework.data.redis.core.query.SortQueryBuilder;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;


/**
 * 通用缓存工具类
 *
 * @Dec: 使用RedisTemplateAPI
 * 方法	                  子API接口	                  描述
 * opsForValue()	    ValueOperations	         描述具有简单值的条目
 * opsForList()	        ListOperations	         操作具有list值的条目
 * opsForSet()	        SetOperations	         操作具有set值的条目
 * opsForZSet() 	    ZSetOperations	         操作具有ZSet值（排序的set）的条目
 * opsForHash()	        HashOperations	         操作具有hash值的条目
 * boundValueOps(K)   	BoundValueOperations	 以绑定指定key的方式，操作具有简单值的条目
 * boundListOps(K)	    BoundListOperations	     以绑定指定key的方式，操作具有list的条目
 * boundSetOps(K)	    BoundSetOperations	     以绑定指定key的方式，操作具有set的条目
 * boundZSet(K)	        BoundZSetOperations	     以绑定指定key的方式，操作具有ZSet（排序的set）的条目
 * boundHashOps(K)	    BoundHashOperations	     以绑定指定key的方式，操作具有hash值的条目
 * @author:
 * @date :2017/11/28
 */
@Component
public class RedisUtils {
    //最常用stringRedisTemplate
    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    //普通泛型redisTemplate
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;


    /**
     * 模糊查询keys
     *
     * @param pattern
     * @return
     */
    public Set<String> keys(String pattern) {
        return redisTemplate.keys(pattern);
    }

    /**
     * 指定缓存的失效时间
     *
     * @param key  缓存KEY
     * @param time 失效时间(秒)
     */
    public void expire(String key, long time) {
        if (time > 0) {
            redisTemplate.expire(key, time, TimeUnit.SECONDS);
        }
    }

    /**
     * 删除缓存<br>
     * 根据key精确匹配删除
     *
     * @param key
     */
    @SuppressWarnings("unchecked")
    public void del(String... key) {
        if (key != null && key.length > 0) {
            if (key.length == 1) {
                redisTemplate.delete(key[0]);
            } else {
                redisTemplate.delete(CollectionUtils.arrayToList(key));
            }
        }
    }

    /**
     * 批量删除<br>
     * （该操作会执行模糊查询，请尽量不要使用，以免影响性能或误删）
     *
     * @param pattern
     */
    public void batchDel(String... pattern) {
        for (String kp : pattern) {
            redisTemplate.delete(redisTemplate.keys(kp + "*"));
        }
    }

    /**
     * 取得缓存（int型）
     *
     * @param key
     * @return
     */
    public Integer getInt(String key) {
        String value = stringRedisTemplate.boundValueOps(key).get();
        if (value != "") {
            return Integer.valueOf(value);
        }
        return null;
    }

    /**
     * 取得缓存（字符串类型）
     *
     * @param key
     * @return
     */
    public String getStr(String key) {
        return stringRedisTemplate.boundValueOps(key).get();
    }

    /**
     * 取得缓存（字符串类型）
     *
     * @param key
     * @return
     */
    public String getStr(String key, boolean retain) {
        String value = stringRedisTemplate.boundValueOps(key).get();
        if (!retain) {
            redisTemplate.delete(key);
        }
        return value;
    }

    /**
     * 获取缓存<br>
     * 注：基本数据类型(Character除外)，请直接使用get(String key, Class<T> clazz)取值
     *
     * @param key
     * @return
     */
    public Object getObj(String key) {
        return redisTemplate.boundValueOps(key).get();
    }

    /**
     * 获取缓存<br>
     * 注：java 8种基本类型的数据请直接使用get(String key, Class<T> clazz)取值
     *
     * @param key
     * @param retain 是否保留
     * @return
     */
    public Object getObj(String key, boolean retain) {
        Object obj = redisTemplate.boundValueOps(key).get();
        if (!retain) {
            redisTemplate.delete(key);
        }
        return obj;
    }

    /**
     * 获取缓存<br>
     * 注：该方法暂不支持Character数据类型
     *
     * @param key   key
     * @param clazz 类型
     * @return
     */
    @SuppressWarnings("unchecked")
    public <T> T get(String key, Class<T> clazz) {
        return (T) redisTemplate.boundValueOps(key).get();
    }

    /**
     * 获取缓存json对象<br>
     *
     * @param key   key
     * @param clazz 类型
     * @return
     */
    public <T> T getJson(String key, Class<T> clazz) {
        return JsonMapper.fromJsonString(stringRedisTemplate.boundValueOps(key).get(), clazz);
    }

    /**
     * 将value对象写入缓存
     *
     * @param key
     * @param value
     * @param time  失效时间(秒)
     */
    public void set(String key, Object value, long time) {
        if (value.getClass().equals(String.class)) {
            stringRedisTemplate.opsForValue().set(key, value.toString());
        } else if (value.getClass().equals(Integer.class)) {
            stringRedisTemplate.opsForValue().set(key, value.toString());
        } else if (value.getClass().equals(Double.class)) {
            stringRedisTemplate.opsForValue().set(key, value.toString());
        } else if (value.getClass().equals(Float.class)) {
            stringRedisTemplate.opsForValue().set(key, value.toString());
        } else if (value.getClass().equals(Short.class)) {
            stringRedisTemplate.opsForValue().set(key, value.toString());
        } else if (value.getClass().equals(Long.class)) {
            stringRedisTemplate.opsForValue().set(key, value.toString());
        } else if (value.getClass().equals(Boolean.class)) {
            stringRedisTemplate.opsForValue().set(key, value.toString());
        } else {
            redisTemplate.opsForValue().set(key, value);
        }
        if (time > 0) {
            redisTemplate.expire(key, time, TimeUnit.SECONDS);
        }
    }

    /**
     * 将value对象以JSON格式写入缓存
     *
     * @param key
     * @param value
     * @param time  失效时间(秒)
     */
    public void setJson(String key, Object value, long time) {
        stringRedisTemplate.opsForValue().set(key, JsonMapper.toJsonString(value));
        if (time > 0) {
            stringRedisTemplate.expire(key, time, TimeUnit.SECONDS);
        }
    }

    /**
     * 更新key对象field的值
     *
     * @param key   缓存key
     * @param field 缓存对象field
     * @param value 缓存对象field值
     */
    public void setJsonField(String key, String field, String value) {
        JSONObject obj = JSON.parseObject(stringRedisTemplate.boundValueOps(key).get());
        obj.put(field, value);
        stringRedisTemplate.opsForValue().set(key, obj.toJSONString());
    }


    /**
     * 递减操作
     *
     * @param key
     * @param by
     * @return
     */
    public double decr(String key, double by) {
        return redisTemplate.opsForValue().increment(key, -by);
    }

    public double decr(String key, Integer by) {
        return redisTemplate.opsForValue().increment(key, -by);
    }

    public double decr(String key, long by) {
        return redisTemplate.opsForValue().increment(key, -by);
    }

    /**
     * 递增操作
     *
     * @param key
     * @param by
     * @return
     */
    public double incr(String key, double by) {
        return redisTemplate.opsForValue().increment(key, by);
    }

    public double incr(String key, Integer by) {
        return redisTemplate.opsForValue().increment(key, by);
    }

    public double incr(String key, long by) {
        return redisTemplate.opsForValue().increment(key, by);
    }

    /**
     * 获取double类型值
     *
     * @param key
     * @return
     */
    public double getDouble(String key) {
        String value = stringRedisTemplate.boundValueOps(key).get();
        if (value != "") {
            return Double.valueOf(value);
        }
        return 0d;
    }

    /**
     * 设置double类型值
     *
     * @param key
     * @param value
     * @param time  失效时间(秒)
     */
    public void setDouble(String key, double value, long time) {
        stringRedisTemplate.opsForValue().set(key, String.valueOf(value));
        if (time > 0) {
            stringRedisTemplate.expire(key, time, TimeUnit.SECONDS);
        }
    }

    /**
     * 设置double类型值
     *
     * @param key
     * @param value
     * @param time  失效时间(秒)
     */
    public void setInt(String key, int value, long time) {
        stringRedisTemplate.opsForValue().set(key, String.valueOf(value));
        if (time > 0) {
            stringRedisTemplate.expire(key, time, TimeUnit.SECONDS);
        }
    }

    //===========================================HASH操作=====================================================

    /**
     * 将map写入缓存
     *
     * @param key
     * @param map
     * @param time 失效时间(秒)
     */
    public <T> void setMap(String key, Map<String, T> map, long time) {
        redisTemplate.opsForHash().putAll(key, map);
    }

    /**
     * 将map写入缓存
     *
     * @param key
     */
    @SuppressWarnings("unchecked")
    public <T> void setMap(String key, T obj) {
        Map<String, String> map = (Map<String, String>) JsonMapper.parseObject(obj, Map.class);
        redisTemplate.opsForHash().putAll(key, map);
    }

    /**
     * 向key对应的map中添加缓存对象
     *
     * @param key
     * @param map
     */
    public <T> void addMap(String key, Map<String, T> map) {
        redisTemplate.opsForHash().putAll(key, map);
    }

    /**
     * 向key对应的map中添加缓存对象
     *
     * @param key   cache对象key
     * @param field map对应的key
     * @param value 值
     */
    public void addMap(String key, String field, String value) {
        redisTemplate.opsForHash().put(key, field, value);
    }

    /**
     * 向key对应的map中添加缓存对象
     *
     * @param key   cache对象key
     * @param field map对应的key
     * @param obj   对象
     */
    public <T> void addMap(String key, String field, T obj) {
        redisTemplate.opsForHash().put(key, field, obj);
    }

    /**
     * 获取map缓存
     *
     * @param key
     * @param clazz
     * @return
     */
    public <T> Map<String, T> mget(String key, Class<T> clazz) {
        BoundHashOperations<String, String, T> boundHashOperations = redisTemplate.boundHashOps(key);
        return boundHashOperations.entries();
    }

    /**
     * 获取map缓存
     *
     * @param key
     * @param clazz
     * @return
     */
    public <T> T getMap(String key, Class<T> clazz) {
        BoundHashOperations<String, String, String> boundHashOperations = redisTemplate.boundHashOps(key);
        Map<String, String> map = boundHashOperations.entries();
        return JsonMapper.parseObject(map, clazz);
    }

    /**
     * 获取map缓存中的某个对象
     *
     * @param key
     * @param field
     * @param clazz
     * @return
     */
    @SuppressWarnings("unchecked")
    public <T> T getMapField(String key, String field, Class<T> clazz) {
        return (T) redisTemplate.boundHashOps(key).get(field);
    }

    /**
     * 删除map中的某个对象
     *
     * @param key   map对应的key
     * @param field map中该对象的key
     */
    public void delMapField(String key, String... field) {
        BoundHashOperations<String, String, ?> boundHashOperations = redisTemplate.boundHashOps(key);
        boundHashOperations.delete(field);
    }
    //===========================================HASH操作结束=====================================================

    //===========================================SET操作=====================================================

    /**
     * 添加set
     *
     * @param key
     * @param value
     */
    public void sadd(String key, String... value) {
        redisTemplate.boundSetOps(key).add(value);
    }

    /**
     * 删除set集合中的对象
     *
     * @param key
     * @param value
     */
    public void srem(String key, String... value) {
        redisTemplate.boundSetOps(key).remove(value);
    }

    /**
     * set重命名
     *
     * @param oldkey
     * @param newkey
     */
    public void srename(String oldkey, String newkey) {
        redisTemplate.boundSetOps(oldkey).rename(newkey);
    }

    //===========================================SET操作结束=====================================================

    //===========================================LIST操作=====================================================

    /**
     * 写入list
     *
     * @param key
     * @param value 命令实体类
     */
    public void addElementOfList(final String key, Object value) {
        redisTemplate.opsForList().rightPush(key, value);
    }

    /**
     * 根据键值获取队列大小
     *
     * @param key redis的键
     * @return 队列大小
     */
    public Long getSizeOfList(String key) {
        return stringRedisTemplate.opsForList().size(key);
    }

    /**
     * list/set排序
     *
     * @param key
     * @return
     */
    public List<String> sortBy(String key) {
        SortQueryBuilder<String> builder = SortQueryBuilder.sort(key);
        builder.alphabetical(true);//对字符串使用“字典顺序”
        return stringRedisTemplate.sort(builder.build());
    }

    /**
     * 外部设置sort条件，传入作为方法参数
     *
     * @param builder
     * @return
     */
    public List<String> sortBy(SortQueryBuilder<String> builder) {
        return stringRedisTemplate.sort(builder.build());
    }

    /**
     * 排序完，存储至新的集合中
     *
     * @param builder
     * @param storeKey
     * @return
     */
    public Long sortByAndStore(SortQueryBuilder<String> builder, String storeKey) {
        return stringRedisTemplate.sort(builder.build(), storeKey);
    }

    /**
     * 实现分页排序,只针对集合中存储的是String类型，如果是泛型，则会出现类型转换错误
     *
     * @param key
     * @param strIndex
     * @param endIndex
     * @return
     */
    public List<String> sortPageBy(String key, long strIndex, long endIndex) {
        SortQueryBuilder<String> builder = SortQueryBuilder.sort(key);
        builder.alphabetical(true);//对字符串使用“字典顺序”
        builder.limit(strIndex, endIndex);

        return stringRedisTemplate.sort(builder.build());
    }

    /**
     * 实现分页，如需要排序需要提前排好后存入队列
     *key的生产策略，表明加上查询条件和排序条件 ，这样针对不同的查询进行缓存，提高查询速率
     * @param key
     * @param strIndex
     * @param endIndex
     * @return
     */
    public <T> List<T> PageBy(String key, long strIndex, long endIndex) {
        return (List<T>) redisTemplate.opsForList().range(key,strIndex,endIndex);
    }

    /**
     * 降序排序
     *
     * @param key
     * @return
     */
    public List<String> sortDescBy(String key) {
        SortQueryBuilder<String> builder = SortQueryBuilder.sort(key);
        builder.alphabetical(true);//对字符串使用“字典顺序”
        builder.order(SortParameters.Order.DESC);

        return stringRedisTemplate.sort(builder.build());
    }

    //===========================================LIST操作结束=====================================================

    /**
     * 短信缓存(后续可能会用上)
     *
     * @param key
     * @param value
     * @param time
     */
    public void setIntForPhone(String key, Object value, int time) {
        stringRedisTemplate.opsForValue().set(key, JsonMapper.toJsonString(value));
        if (time > 0) {
            stringRedisTemplate.expire(key, time, TimeUnit.SECONDS);
        }
    }
    /**
     * 判断key是否存在
     *
     * @param key 键
     * @return true 存在 false不存在
     */
    public boolean hasKey(String key) {
        try {
            return redisTemplate.hasKey(key);
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }
}